package com.acme.bankapp.network;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Bank Acceptor implements single thread network app.
 * Protocol simple plain text with command value
 * Protocol support few message:
 * 	- get client by Name
 *  - get all clients in bank
 *  - modify clients (add/remove accounts, change name, gender) ??
 *  - modify accounts (deposit, withdraw) ??
 *  - backup bank state
 *  - restore bank state from binary file
 * @author Alekov Andrey
 *
 */
public class BankAcceptor {
	final static Logger logger = LoggerFactory.getLogger(BankAcceptor.class);
	private BankApplication bankApplication;
	private Properties settings;
	/**
	 * Constructor
	 */
	BankAcceptor(Properties acceptorSettings, BankApplication bankApp) {
		this.bankApplication = bankApp;
		this.settings = acceptorSettings;
	}
	
	/**
	 * Run acceptor.
	 */
	void run() {
		if (this.bankApplication == null) {
			throw new IllegalArgumentException("Bank application are not initialized");
		}
		try (ServerSocket providerSocket = new ServerSocket(
				Integer.parseInt(settings.getProperty("Port")),
				10)){
			logger.info("Waiting for connection");
			Socket connection = providerSocket.accept();
			logger.info("Connection received from " + connection.getInetAddress().getHostName());
			logger.info("Connection successful");
			
			ObjectOutputStream outStream = new ObjectOutputStream(connection.getOutputStream());
			ObjectInputStream inStream = new ObjectInputStream(connection.getInputStream());
			
			// Crack message and call BankApplication
			boolean isClientExit = false;
			do {
				try {
					Message message = (Message) inStream.readObject();
					logger.info("<< " + message.toString());
					switch (message.getHeader()) {
					case "CLIENT.ADD":
						this.bankApplication.onClientAdd(message.getValue(), outStream);
						break;
					case "CLIENT.CHANGE":
						this.bankApplication.onClientChange(message.getValue(), outStream);
						break;
					case "CLIENT.GET":
						this.bankApplication.onClientGet(message.getValue(), outStream);
						break;
					case "ACCOUNT.ADD":
						this.bankApplication.onAccountAdd(message.getValue(), outStream);
					case "ACCOUNT.CHANGE":
						this.bankApplication.onAccountChange(message.getValue(), outStream);
						break;
					case "ACCOUNT.DEPOSIT":
						this.bankApplication.onAccountDeposit(message.getValue(), outStream);
						break;
					case "ACCOUNT.WITHDRAW":
						this.bankApplication.onAccountWithDraw(message.getValue(), outStream);
						break;
					case "EXIT":
						logger.info("Client say to us good bye!");
						isClientExit = true;
						break;
					default:
						break;
					}
				} catch (ClassNotFoundException e) {
					logger.error("Cannot read message...");
					e.printStackTrace();
				}
			} while (!isClientExit);
			outStream.close();
			inStream.close();
		} catch (IOException ioException) {
			logger.error(ioException.getMessage());
			ioException.printStackTrace();
		}
	}

	/**
	 * Main. Settings from file created just for fun. 
	 * Design for study.
	 * @param args
	 */
	public static void main(final String args[]) {
		Properties settings = new Properties();
		try (FileInputStream in = new FileInputStream("server.settings")) {
			settings.load(in);
			in.close();
		}
		catch (IOException e) {
			logger.error("BankServer settings not found");
			e.printStackTrace();
		}
		BankAcceptor server = new BankAcceptor(settings,  new DefaultBankApplicationImpl());
		while (true) {
			server.run();
		}
	}

}
